package org.chintoooor.trio.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import org.chintoooor.trio.server.accept.task.Accept;
import org.chintoooor.trio.server.accept.task.AcceptorTaskModel;
import org.chintoooor.trio.server.http.task.Http;
import org.chintoooor.trio.server.http.task.HttpTaskModel;

public class Server {

	public static final int LISTENERS_THREADS = 1;
	public static final int ACCEPT_THREADS = 1;
	public static final int TIMEOUT = 360000;

	private ExecutorService executorService;

	private AtomicBoolean isUp = new AtomicBoolean(true);
	private ServerSocketChannel serverSocketChannel;
	private Selector acceptSelector;
	private List<HttpTaskModel> listenerThreadModels;
	private Future<?> acceptFuture;
	private List<Future<?>> readFutures;

	public Server() {
		super();
	}

	private void initializeThreads() {
		executorService = Executors.newFixedThreadPool(LISTENERS_THREADS
				+ ACCEPT_THREADS);

	}

	private void initializeSelectors() throws IOException {

		listenerThreadModels = new ArrayList<HttpTaskModel>(
				LISTENERS_THREADS);

		readFutures = new ArrayList<Future<?>>(LISTENERS_THREADS);

		for (int i = 0; i < LISTENERS_THREADS; i++) {
			Selector selector = Selector.open();
			Collection<SocketChannel> unregisteredChannels = new LinkedList<SocketChannel>();
			HttpTaskModel selectionModel = new HttpTaskModel(
					selector, unregisteredChannels);
			listenerThreadModels.add(selectionModel);
		}

		acceptSelector = Selector.open();

	}

	private void initializeSockets() throws IOException {
		serverSocketChannel = ServerSocketChannel.open();
		SocketAddress localHost_8888 = new InetSocketAddress("localhost", 8888);

		serverSocketChannel.bind(localHost_8888);
		serverSocketChannel.configureBlocking(false);

		serverSocketChannel.register(acceptSelector, SelectionKey.OP_ACCEPT);
	}

	private void initializeControllers() {
	}

	private void init() throws IOException {
		initializeThreads();
		initializeSelectors();
		initializeSockets();
		initializeControllers();
	}

	private void start() {
		for (HttpTaskModel currentReadSelectionModel : listenerThreadModels) {
			Runnable http = new Http(isUp, currentReadSelectionModel);
			Future<?> readWriteFuture = executorService.submit(http);
			readFutures.add(readWriteFuture);
		}

		AcceptorTaskModel acceptorThreadModel = new AcceptorTaskModel(
				acceptSelector, listenerThreadModels);
		Runnable accept = new Accept(isUp, acceptorThreadModel);
		acceptFuture = executorService.submit(accept);
	}

	private void listen() {
	}

	private void shutdown() throws InterruptedException {
		isUp.set(false);

		for (Future<?> readWriteFuture : readFutures) {
			try {
				readWriteFuture.get();
			} catch (ExecutionException ee) {
				ee.printStackTrace();
			} catch (InterruptedException ie) {
				ie.printStackTrace();
			}
		}
		try {
			acceptFuture.get();
		} catch (ExecutionException ee) {
			ee.printStackTrace();
		} catch (InterruptedException ie) {
			ie.printStackTrace();
		}
		executorService.shutdown();
		executorService.awaitTermination(TIMEOUT, TimeUnit.MILLISECONDS);
	}

	/**
	 * @param args
	 * @throws IOException
	 * @throws InterruptedException
	 */
	public static void main(String[] args) throws IOException,
			InterruptedException {
		Server server = new Server();
		server.init();
		server.start();
		server.listen();
		Thread.sleep(TIMEOUT * 6);
		server.shutdown();
	}

}